package com.ysstech.etfmanage.thread;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.ysstech.common.cache.MapCacheManager;
import com.ysstech.common.exception.BusinessException;
import com.ysstech.common.service.HolidayService;
import com.ysstech.common.util.BigDecimalUtil;
import com.ysstech.common.util.DateUtil;
import com.ysstech.etfmanage.entity.Account;
import com.ysstech.etfmanage.entity.AccountFill;
import com.ysstech.etfmanage.entity.AccountVo;
import com.ysstech.etfmanage.entity.ParamVo;
import com.ysstech.etfmanage.entity.Rights;
import com.ysstech.etfmanage.enums.EtfManageEnum;
import com.ysstech.etfmanage.service.AbsEtfAccountGenerate;
import com.ysstech.etfmanage.service.AccountFillService;
import com.ysstech.etfmanage.service.AccountService;
import com.ysstech.etfmanage.service.ParamService;
import com.ysstech.etfmanage.service.RightsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

@Slf4j
@Service
public abstract class EtfAccountGenerateService<T> implements AbsEtfAccountGenerate<T> {
    @Autowired
    private AccountService accountService;
    @Autowired
    private AccountFillService accountFillService;
    @Autowired
    private RightsService rightsService;
    @Autowired
    private HolidayService holidayService;
    @Autowired
    private ParamService paramService;

    @Transactional(rollbackFor = Exception.class)
    public void doGenerate(Account vo, ParamVo paramVo) throws Exception {
        MapCacheManager.getInstance().removeCache(vo.getPurchaseDate());
        updateEtfAccountClearCode(vo, paramVo);
        restoreEtfAccount(vo, paramVo);
        List<T> voList = this.initEtfAccount(vo, paramVo);
        if (!CollectionUtils.isEmpty(voList)) {
            for (T t : voList) {
                AccountVo accountVo = (AccountVo) t;
                handleInitDataEtfaccount(accountVo.getList(), accountVo.getSumReplaceMoney());
            }
        }
        List<T> voListRight = this.renewRightEtfAccount(vo, paramVo);
        if (!CollectionUtils.isEmpty(voListRight)) {

        }
        List<T> voListFill = this.renewFillEtfAccount(vo, paramVo);
        if (!CollectionUtils.isEmpty(voListFill)) {
            for (T t : voListFill) {
                AccountVo accountVo = (AccountVo) t;
                accountFillService.etfAccountFill(accountVo.getList(), accountVo.getParamVo(), accountVo.getMapFillDetail());
            }
        }
    }

    /**
     * 还原台账数据
     *
     * @param vo
     * @param paramVo
     * @throws Exception
     */
    public void restoreEtfAccount(Account vo, ParamVo paramVo) throws Exception {
        if (null == vo || null == paramVo
                || StringUtils.isBlank(vo.getAccountId()) || null == vo.getPurchaseDate()
                || null == paramVo.getEtfType() || null == paramVo.getEtfMode()
                || StringUtils.isBlank(paramVo.getEtfMarket())
        ) {
            log.info("还原ETF台账失败，获取ETF基金信息失败！");
            throw new BusinessException("更新ETF台账失败，获取ETF基金信息失败！");
        }
        // 删除补票数据，按照补票日期删除
        QueryWrapper<AccountFill> wrapperFill = new QueryWrapper<>();
        wrapperFill.ge("FDATE", vo.getPurchaseDate());
        wrapperFill.exists("SELECT ID FROM ETF_ACCOUNT a  where  a.ACCOUNT_ID='" + vo.getAccountId() + "'" +
                " AND a.ETF_TYPE = " + paramVo.getEtfType() + " AND a.ETF_MODE=" + paramVo.getEtfMode() + " AND a.ETF_MARKET='" + paramVo.getEtfMarket() + "'");
        accountFillService.remove(wrapperFill);
        // 删除权益数据，按照补票日期删除
        QueryWrapper<Rights> wrapperRights = new QueryWrapper<>();
        wrapperRights.ge("FDATE", vo.getPurchaseDate());
        wrapperRights.exists("SELECT ID FROM ETF_ACCOUNT a  where a.ACCOUNT_ID='" + vo.getAccountId() + "'" +
                " AND a.ETF_TYPE = " + paramVo.getEtfType() + " AND a.ETF_MODE=" + paramVo.getEtfMode() + " AND a.ETF_MARKET='" + paramVo.getEtfMarket() + "'");
        rightsService.remove(wrapperRights);
        //删除台账数据 按照申购日期删除
        QueryWrapper<Account> wrapper = new QueryWrapper<>();
        wrapper.eq("ETF_TYPE", paramVo.getEtfType());
        wrapper.eq("ETF_MODE", paramVo.getEtfMode());
        wrapper.eq("ETF_MARKET", paramVo.getEtfMarket());
        wrapper.eq("ACCOUNT_ID", vo.getAccountId());
        wrapper.ge("FDATE", vo.getPurchaseDate());
        accountService.remove(wrapper);
    }

    /**
     * 修改清算标志
     *
     * @param vo
     * @param paramVo
     * @throws Exception
     */
    public void updateEtfAccountClearCode(Account vo, ParamVo paramVo) throws Exception {
        List<String> list = this.getMarketList();
        if (CollectionUtils.isEmpty(list)) {
            //修改条件
            String ftkdate = holidayService.getNextWorkday(vo.getPurchaseDate(), paramVo.getPayableReplaceMoneyDays());
            UpdateWrapper<Account> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("REFUND_DATE", ftkdate);
            updateWrapper.eq("ETF_TYPE", paramVo.getEtfType());
            updateWrapper.eq("ETF_MODE", paramVo.getEtfMode());
            updateWrapper.eq("ETF_MARKET", paramVo.getEtfMarket());
            updateWrapper.eq("ACCOUNT_ID", vo.getAccountId());
            updateWrapper.eq("CLEAR_CODE", EtfManageEnum.ETF_CLEAR_CODE_YES.getCode());
            Account account = new Account();
            account.setClearCode(EtfManageEnum.ETF_CLEAR_CODE_NO.getCode());
            account.setRefundDate(null);
            accountService.update(account, updateWrapper);
        } else {
            for (String maket : list) {
                if (StringUtils.isBlank(maket)) {
                    continue;
                }
                paramVo = paramService.getEtfParamVo(maket, paramVo);
                //修改条件
                String ftkdate = holidayService.getNextWorkday(vo.getPurchaseDate(), paramVo.getPayableReplaceMoneyDays());
                UpdateWrapper<Account> updateWrapper = new UpdateWrapper<>();
                updateWrapper.eq("REFUND_DATE", ftkdate);
                updateWrapper.eq("ETF_TYPE", paramVo.getEtfType());
                updateWrapper.eq("ETF_MODE", paramVo.getEtfMode());
                updateWrapper.eq("ETF_MARKET", paramVo.getEtfMarket());
                updateWrapper.eq("ACCOUNT_ID", vo.getAccountId());
                updateWrapper.eq("CLEAR_CODE", EtfManageEnum.ETF_CLEAR_CODE_YES.getCode());
                Account account = new Account();
                account.setClearCode(EtfManageEnum.ETF_CLEAR_CODE_NO.getCode());
                account.setRefundDate(null);
                accountService.update(account, updateWrapper);
            }
        }
    }

    /**
     * 插入ETF台账数据，每个明细券插入一个汇总数据
     *
     * @param list
     * @return
     * @throws Exception
     */
    public void handleInitDataEtfaccount(List<Account> list, BigDecimal sumReplaceMoney) throws Exception {
        List<Account> etfaccountList = new ArrayList<>();
        Map<String, List<Account>> mapaccount = this.etfhandleDataEtfaccount(list);
        if (CollectionUtils.isEmpty(mapaccount)) {
            log.info("没有需要初始化的数据");
            return;
        }
        int size = 0; //计数map是不是到最后条
        BigDecimal countReplaceMoney = BigDecimal.ZERO;
        //循环的是每个股票当天需要替代的
        for (Map.Entry<String, List<Account>> info : mapaccount.entrySet()) {
            List<Account> accounts = info.getValue();
            if (CollectionUtils.isEmpty(accounts)) {
                continue;
            }
            BigDecimal totalAmount = BigDecimal.ZERO, totalReplaceMoney = BigDecimal.ZERO, totalReplaceableMoney = BigDecimal.ZERO; //汇总的数量和替代金额,单位成本
            String sumId = DateUtil.generateUUID();//先生成汇总记录的id 排序汇总就会在前面
            Account lastshareAccount = null;
            for (int j = 0; j < accounts.size(); j++) {
                Account accountVo = accounts.get(j);
                if (null == accountVo) {
                    continue;
                }
                boolean islast = (j == accounts.size() - 1 && size == mapaccount.size() - 1);
                if (null != sumReplaceMoney && sumReplaceMoney.compareTo(BigDecimal.ZERO) > 0 && islast) {  //最后一条
                    accountVo.setReplaceMoney(BigDecimalUtil.subtract(sumReplaceMoney, countReplaceMoney));
                } else {
                    countReplaceMoney = countReplaceMoney.add(accountVo.getReplaceMoney());
                }
                totalAmount = totalAmount.add(accountVo.getAmount());//汇总 替代金额和申购数量
                totalReplaceMoney = totalReplaceMoney.add(accountVo.getReplaceMoney());
                totalReplaceableMoney = totalReplaceableMoney.add(accountVo.getReplaceableMoney());
                if (j == accounts.size() - 1) {
                    lastshareAccount = accountVo;
                }
                etfaccountList.add(accountVo);
            }
            //每处理完一只股票，则往台账库里写一条股票的数据。申购的可退替代金额 汇总的记录 申购数量和申购的替金额 汇总的记录
            if (null != lastshareAccount) {
                Account accountVo = new Account();
                accountVo.setSecurityName(lastshareAccount.getSecurityName());
                accountVo.setSecurityCode(lastshareAccount.getSecurityCode());
                accountVo.setShareholderCode(EtfManageEnum.ETF_STOCKHOLDER_CODE.getCode()); //股东代码
                accountVo.setFdate(lastshareAccount.getFdate());
                accountVo.setBusinessType(lastshareAccount.getBusinessType());
                accountVo.setSurplusAmount(totalAmount);
                accountVo.setAmount(totalAmount);
                accountVo.setReplaceMoney(totalReplaceMoney);
                accountVo.setReplaceableMoney(totalReplaceableMoney);
                accountVo.setUnitCost(lastshareAccount.getUnitCost());
                accountVo.setDealSeat(EtfManageEnum.ETF_STOCKHOLDER_CODE.getCode());
                accountVo.setTransactionNumber(EtfManageEnum.ETF_STOCKHOLDER_CODE.getCode());
                accountVo.setClearCode(EtfManageEnum.ETF_CLEAR_CODE_NO.getCode());
                accountVo.setMarket(lastshareAccount.getMarket());
                accountVo.setAccountId(lastshareAccount.getAccountId());
                accountVo.setAddTime(LocalDateTime.now());
                accountVo.setEtfType(lastshareAccount.getEtfType());
                accountVo.setEtfMode(lastshareAccount.getEtfMode()); //ETF类型
                accountVo.setEtfMarket(lastshareAccount.getEtfMarket());
                accountVo.setId(sumId);
                accountVo.setRate(lastshareAccount.getRate());
                etfaccountList.add(accountVo);
            }
            size++;
        }
        if (!CollectionUtils.isEmpty(etfaccountList)) {
            accountService.saveBatch(etfaccountList);
            log.info("更新台账数据成功，总共" + etfaccountList.size() + "条");
        }
    }


    /**
     * 将待补的一个券拆分成 按照申购日期的集合，分别去补
     *
     * @param list
     * @return
     */
    public Map<String, List<Account>> etfhandleDataEtfaccount(List<Account> list) throws Exception {
        Map<String, List<Account>> map = new LinkedHashMap<>();
        if (CollectionUtils.isEmpty(list)) {
            return map;
        }
        for (Account etfaccount : list) {
            String key = etfaccount.getSecurityCode() + "\t" + etfaccount.getFdate() + "\t"
                    + etfaccount.getBusinessType() + "\t" + etfaccount.getMarket();
            List<Account> listetf = null;
            if (map.containsKey(key)) {
                listetf = map.get(key);
                if (CollectionUtils.isEmpty(listetf)) {
                    continue;
                }
                listetf.add(etfaccount);
                map.put(key, listetf);
            } else {
                listetf = new ArrayList<>();
                listetf.add(etfaccount);
                map.put(key, listetf);
            }
        }
        return map;
    }

}
